<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>ElementPlus 设计器组件包装器</title>
  <link rel="stylesheet" href="node_modules/element-plus/dist/index.css">
  <script src="node_modules/vue/dist/vue.global.js"></script>
  <script src="node_modules/element-plus/dist/index.full.js"></script>
  <script src="node_modules/sortablejs/Sortable.min.js"></script>
  <script src="https://unpkg.com/@element-plus/icons-vue"></script>
  <script src="https://unpkg.com/interactjs/dist/interact.min.js"></script>

  <style>
    body {
      margin: 0%;
    }

    .designer-container {
      display: flex;
      width: 100vw;
      height: 100vh;
      background: #ebebec;
      font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Microsoft YaHei', sans-serif;
    }

    .toolbox {
      width: 200px;
      background: #fff;
      border-right: 1px solid #e4e7ed;
      padding: 4px;
      overflow-x: hidden;
      overflow-y: auto;
      box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
      display: absolute;
    }

    .designer-viewport {
      flex: 1;
      position: relative;
      background-color: #0f0f0f;
      overflow: hidden;
      padding: 0px;
    }



    /* 包装器主体 - 无尺寸 */
    .component-wrapper {
      position: absolute;
      border: 1px solid transparent;
      /* 初始无边框 */
    }

    /* 内容区域 - 决定包装器尺寸 */
    .content-area {
      display: inline-block;
      /* 尺寸由内部组件决定 */
    }

    /* 浮动拖拽控制条 */
    .wrapper-handle {
      position: absolute;
      top: -28px;
      /* 位于组件上方 */
      left: 0;
      width: 100%;
      height: 28px;
      border-radius: 6px 6px 0 0;
      z-index: 10;
      cursor: move;
      color: #fff;

      /* 初始隐藏 */
      opacity: 0;
      visibility: hidden;
      transform: translateY(10px);
      transition: all 0.3s ease;
    }

    /* 当组件被选中时显示 */
    .component-wrapper.selected .wrapper-handle {
      opacity: 1;
      visibility: visible;
      transform: translateY(0);
    }


    /* 浮动缩放点 - 核心实现 */
    .resize-handle {
      position: absolute;
      display: inline-block;
      width: var(--handle-size);
      height: var(--handle-size);
      background-color: var(--primary-color);
      border: 2px solid white;
      border-radius: 50%;
      box-shadow: 0 0 4px rgba(0, 0, 0, 0.2);
      cursor: pointer;
      z-index: 10;
      transform: translate(-50%, -50%);
      transition: all 0.2s ease;
      /* 初始隐藏 */
      opacity: 0;
      visibility: hidden;
    }

    /* 当组件被选中时显示 */
    .component-wrapper.selected .resize-handle {
      opacity: 1;
      visibility: visible;
    }

    /* 控制点悬浮效果 */
    .resize-handle:hover {
      background-color: #66b1ff;
      box-shadow: 0 0 6px rgba(0, 0, 0, 0.3);
      z-index: 11;
    }


    /* 修改缩放控制点样式 */
    .resize-handle {
      position: absolute;
      width: 4px;
      height: 4px;
      background-color: #409eff;
      border: 4px solid white;
      border-radius: 50%;
      box-shadow: 0 0 2px rgba(0, 0, 0, 0.2);
      cursor: pointer;
      z-index: 10;
      opacity: 0;
      visibility: hidden;
      transition: all 0.2s ease;
    }

    /* 调整控制点位置 - 紧贴组件边缘 */
    .resize-nw {
      top: 0;
      left: 0;
      cursor: nw-resize;
      transform: translate(-50%, -50%);
    }

    .resize-ne {
      top: 0;
      right: 0;
      cursor: ne-resize;
      transform: translate(50%, -50%);
    }

    .resize-sw {
      bottom: 0;
      left: 0;
      cursor: sw-resize;
      transform: translate(-50%, 50%);
    }

    .resize-se {
      bottom: 0;
      right: 0;
      cursor: se-resize;
      transform: translate(50%, 50%);
    }

    /* 缩放时添加视觉反馈 */
    .component-wrapper.resizing {
      outline: 1px dashed #409eff;
      opacity: 0.9;
    }

    .property-panel {
      width: 320px;
      background: #fff;
      border-left: 1px solid #e4e7ed;
      padding: 15px;
      overflow-y: auto;
      box-shadow: -2px 0 8px rgba(0, 0, 0, 0.1);
    }

    .drag-item {
      padding: 8px 12px;
      margin-bottom: 8px;
      background-color: #ecf5ff;
      border: 1px solid #d9ecff;
      border-radius: 4px;
      cursor: move;
    }

    .grid-lines {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      background-image:
        linear-gradient(rgba(180, 175, 175, 0.2) 1px, transparent 1px),
        linear-gradient(90deg, rgba(200, 200, 200, 0.2) 1px, transparent 1px);
      background-size: 20px 20px;
      pointer-events: none;
    }

    .floating-toolbar {
      position: absolute;
      top: 15px;
      right: 15px;
      background: white;
      padding: 8px;
      border-radius: 4px;
      box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
      display: flex;
      gap: 10px;
      z-index: 500;
    }

    .placeholder-component {
      display: flex;
      align-items: center;
      justify-content: center;
      height: 100%;
      color: #909399;
      font-size: 18px;
      background: #fafafa;
      border: 1px dashed #d9d9d9;
    }
  </style>
</head>

<body>
  <div id="app" class="designer-container">
    <!-- 左侧组件面板 -->
    <div class="toolbox">
      <h3 style="margin-top: 0;">组件面板</h3>
      <el-divider></el-divider>
      <el-collapse v-model="activeToolboxGroup" accordion>
        <el-collapse-item title="基础组件" name="1">
          <div class="drag-item" draggable="true" @dragstart="dragStart($event, 'ElButton')">按钮组件</div>
          <div class="drag-item" draggable="true" @dragstart="dragStart($event, 'ElInput')">输入框组件</div>
          <div class="drag-item" draggable="true" @dragstart="dragStart($event, 'ElCard')">卡片组件</div>
        </el-collapse-item>
        <el-collapse-item title="布局组件" name="2">
          <div class="drag-item" draggable="true" @dragstart="dragStart($event, 'ElRow')">网格布局</div>
          <div class="drag-item" draggable="true" @dragstart="dragStart($event, 'ElContainer')">容器组件</div>
        </el-collapse-item>
      </el-collapse>
    </div>

    <!-- 中间设计视图 -->
    <div class="designer-viewport" ref="viewport" @dragover.prevent @drop="handleDrop">
      <div class="grid-lines"></div>
      <div class="floating-toolbar">
        <el-button size="small" @click="clearCanvas">清空画布</el-button>
        <el-button size="small" type="primary" @click="addDemoComponent">添加示例</el-button>
        <el-tag type="info">组件数: {{components.length}}</el-tag>
      </div>

      <div v-for="(comp, index) in components" :key="comp.id" class="component-wrapper"
        :class="{'selected': selectedComponent?.id === comp.id}" :style="{
               left: comp.x + 'px', 
               top: comp.y + 'px', 
               width: comp.width, 
               height: comp.height,
               zIndex: comp.zIndex
             }" @mousedown="selectComponent(comp)">

        <div class="wrapper-handle" v-if="selectedComponent?.id === comp.id" @mousedown="startDrag(comp, $event)">
          <span style="font-size:12px;">{{ comp.type }} #{{ index+1 }}</span>
          <div class="actions">
            <el-icon size="14" @click="removeComponent(comp.id)">
              <Close />
            </el-icon>
          </div>
        </div>

        <div class="content-area">
          <Component :is="comp.type" v-bind="comp.props" />
        </div>
        <!-- 缩放点 - 只在选中时显示 -->
        <template v-if="selectedComponent?.id === comp.id">
          <div class="resize-handle resize-nw" @mousedown="startResize(comp, $event, 'nw')"></div>
          <div class="resize-handle resize-ne" @mousedown="startResize(comp, $event, 'ne')"></div>
          <div class="resize-handle resize-sw" @mousedown="startResize(comp, $event, 'sw')"></div>
          <div class="resize-handle resize-se" @mousedown="startResize(comp, $event, 'se')"></div>
        </template>
      </div>
    </div>

    <!-- 右侧属性面板 -->
    <div class="property-panel" v-if="selectedComponent">
      <h3 style="margin-top: 0;">属性设置</h3>
      <el-divider></el-divider>

      <el-form label-width="80px">
        <el-form-item label="位置X">
          <el-input-number v-model="selectedComponent.x" controls-position="right" size="small"></el-input-number>
        </el-form-item>
        <el-form-item label="位置Y">
          <el-input-number v-model="selectedComponent.y" controls-position="right" size="small"></el-input-number>
        </el-form-item>
        <el-form-item label="宽度">
          <el-input-number v-model="selectedComponent.width" :min="50" controls-position="right"
            size="small"></el-input-number>
        </el-form-item>
        <el-form-item label="高度">
          <el-input-number v-model="selectedComponent.height" :min="30" controls-position="right"
            size="small"></el-input-number>
        </el-form-item>
        <el-form-item label="层级">
          <el-slider v-model="selectedComponent.zIndex" :min="1" :max="100" show-input></el-slider>
        </el-form-item>
      </el-form>

      <el-divider v-if="hasComponentProperties"></el-divider>

      <el-form label-width="80px">
        <template v-if="selectedComponent.type === 'button'">
          <el-form-item label="按钮文本">
            <el-input v-model="selectedComponent.props.text"></el-input>
          </el-form-item>
          <el-form-item label="按钮类型">
            <el-select v-model="selectedComponent.props.type" placeholder="请选择类型">
              <el-option label="默认" value="default"></el-option>
              <el-option label="主要" value="primary"></el-option>
              <el-option label="成功" value="success"></el-option>
              <el-option label="警告" value="warning"></el-option>
              <el-option label="危险" value="danger"></el-option>
            </el-select>
          </el-form-item>
          <el-form-item label="按钮尺寸">
            <el-select v-model="selectedComponent.props.size" placeholder="请选择尺寸">
              <el-option label="大" value="large"></el-option>
              <el-option label="默认" value="default"></el-option>
              <el-option label="小" value="small"></el-option>
            </el-select>
          </el-form-item>
        </template>

        <template v-if="selectedComponent.type === 'input'">
          <el-form-item label="初始值">
            <el-input v-model="selectedComponent.props.value"></el-input>
          </el-form-item>
          <el-form-item label="提示文本">
            <el-input v-model="selectedComponent.props.placeholder"></el-input>
          </el-form-item>
        </template>

        <template v-if="selectedComponent.type === 'card'">
          <el-form-item label="标题">
            <el-input v-model="selectedComponent.props.title"></el-input>
          </el-form-item>
          <el-form-item label="内容">
            <el-input type="textarea" v-model="selectedComponent.props.content"></el-input>
          </el-form-item>
        </template>
      </el-form>

      <el-divider></el-divider>
      <el-button type="danger" plain @click="removeComponent(selectedComponent.id)" style="width:100%">删除组件</el-button>
    </div>
  </div>


</body>
<script>
  const { createApp, ref, reactive, computed } = Vue;
  
  const app = createApp({
    setup() {
      // 组件数据定义
      const components = ref([]);
      const selectedComponent = ref(null);
      const viewport = ref(null);
      const activeToolboxGroup = ref('1');
      let dragData = null;
      let isDragging = false;
      let isResizing = false;
      let resizeData = {
        comp: null,
        direction: '',
        startX: 0,
        startY: 0,
        startWidth: 0,
        startHeight: 0
      };

      // 为组件生成唯一ID
      const generateId = () => Date.now().toString(36) + Math.random().toString(36).substring(2);

      // 计算属性：检查当前选中组件是否有自定义属性
      const hasComponentProperties = computed(() => {
        if (!selectedComponent.value) return false;
        const comp = selectedComponent.value;
        return comp.props;
      });

      // 添加示例组件用于演示
      const addDemoComponent = () => {
        components.value.push({
          id: generateId(),
          type: 'ElButton',
          x: 100,
          y: 100,
          width: "100%",
          height: 40,
          zIndex: 10,
          props: {
            text: true,
            type: 'primary',
            size: 'default',
            innerText: '按钮',
          }
        });

        components.value.push({
          id: generateId(),
          type: 'ElInput',
          x: 250,
          y: 100,
          width: 100,
          height: 40,
          zIndex: 10,
          props: {
            value: '',
            placeholder: '请输入内容...'
          }
        });

        components.value.push({
          id: generateId(),
          type: 'ElCard',
          x: 100,
          y: 200,
          width: 100,
          height: 80,
          zIndex: 10,
          props: {
            header: '卡片标题',
          }
        });
      };

      // 清除画布
      const clearCanvas = () => {
        if (confirm('确定要清空画布吗？所有组件将被移除。')) {
          components.value = [];
          selectedComponent.value = null;
        }
      };

      // 选择组件
      const selectComponent = (comp) => {
        selectedComponent.value = comp;

        // 确保选中的组件位于最顶层
        const maxZIndex = Math.max(...components.value.map(c => c.zIndex), 0);
        if (comp.zIndex < maxZIndex) {
          comp.zIndex = maxZIndex + 1;
        }
      };

      // 开始拖动组件
      const startDrag = (comp, event) => {
        isDragging = true;
        const rect = comp;
        const mouseX = event.clientX;
        const mouseY = event.clientY;

        const offsetX = mouseX - rect.x;
        const offsetY = mouseY - rect.y;

        const onMouseMove = (e) => {
          if (!isDragging) return;
          comp.x = e.clientX - offsetX;
          comp.y = e.clientY - offsetY;
        };
        const onMouseUp = () => {
          document.removeEventListener('mousemove', onMouseMove);
          document.removeEventListener('mouseup', onMouseUp);
          isDragging = false;
        };

        document.addEventListener('mousemove', onMouseMove);
        document.addEventListener('mouseup', onMouseUp);
        event.preventDefault();
      };

      // 开始调整组件尺寸
      const startResize = (comp, event, direction) => {
        event.stopPropagation();
        isResizing = true;

        // 添加缩放状态类
        const wrapper = event.target.closest('.component-wrapper');
        if (wrapper) wrapper.classList.add('resizing');

        resizeData = {
          comp: comp,
          direction: direction,
          startX: event.clientX,
          startY: event.clientY,
          startWidth: comp.width,
          startHeight: comp.height,
          startLeft: comp.x,
          startTop: comp.y
        };

        const onMouseMove = (e) => {
          if (!isResizing || !resizeData.comp) return;

          const deltaX = e.clientX - resizeData.startX;
          const deltaY = e.clientY - resizeData.startY;
          const dir = resizeData.direction;
          // 最小尺寸限制
          const minWidth = 30;
          const minHeight = 20;

          // 根据方向调整尺寸和位置
          if (dir.includes('e')) { // 右侧调整
            const newWidth = Math.max(minWidth, resizeData.startWidth + deltaX);
            resizeData.comp.props.style = { width: `${newWidth}px`, height: `${resizeData.comp.height}px` };
            resizeData.comp.width = newWidth;
          }
          if (dir.includes('w')) { // 左侧调整
            const newWidth = Math.max(minWidth, resizeData.startWidth - deltaX);
            resizeData.comp.props.style = { width: `${newWidth}px`, height: `${resizeData.comp.height}px` };
            resizeData.comp.x = resizeData.startLeft + (resizeData.startWidth - newWidth);
            resizeData.comp.width = newWidth;
          }
          if (dir.includes('s')) { // 下方调整
            const newHeight = Math.max(minHeight, resizeData.startHeight + deltaY);
            resizeData.comp.props.style = { height: `${newHeight}px`, width: `${resizeData.comp.width}px` };
            resizeData.comp.height = newHeight;
          }
          if (dir.includes('n')) { // 上方调整
            const newHeight = Math.max(minHeight, resizeData.startHeight - deltaY);
            resizeData.comp.y = resizeData.startTop + (resizeData.startHeight - newHeight);
            resizeData.comp.height = newHeight;
            resizeData.comp.props.style = { height: `${newHeight}px`, width: `${resizeData.comp.width}px` };
          }
        };

        const onMouseUp = () => {
          document.removeEventListener('mousemove', onMouseMove);
          document.removeEventListener('mouseup', onMouseUp);
          isResizing = false;

          // 移除缩放状态类
          const wrapper = document.querySelector('.component-wrapper.resizing');
          if (wrapper) wrapper.classList.remove('resizing');

          resizeData = null;
        };

        document.addEventListener('mousemove', onMouseMove);
        document.addEventListener('mouseup', onMouseUp);
        event.preventDefault();
      };

      // 处理拖拽放置
      const handleDrop = (event) => {
        event.preventDefault();
        if (!dragData) return;

        const rect = viewport.value.getBoundingClientRect();
        const x = event.clientX- rect.left;
        const y = event.clientY- rect.top;

        components.value.push({
          id: generateId(),
          type: dragData,
          x: x,
          y: y,
          width: "100%",
          height: "100px",
          zIndex: 10,
          props: {style: {width: '100px', height: '50px', backgroundColor: "white" }}
        });

        dragData = null;
      };

      // 设置拖拽开始数据
      const dragStart = (event, componentType) => {
        dragData = componentType;
      };

      // 删除组件
      const removeComponent = (id) => {
        const index = components.value.findIndex(c => c.id === id);
        if (index !== -1) {
          components.value.splice(index, 1);
          if (selectedComponent.value && selectedComponent.value.id === id) {
            selectedComponent.value = null;
          }
        }
      };

      // 初始化
      addDemoComponent();


      return {
        components,
        selectedComponent,
        viewport,
        activeToolboxGroup,
        hasComponentProperties,
        addDemoComponent,
        clearCanvas,
        selectComponent,
        startDrag,
        startResize,
        handleDrop,
        dragStart,
        removeComponent
      };
    }
  });
  // 注册ElementPlus组件
  for (const [key, component] of Object.entries(ElementPlus)) {
    if(key.startsWith('El')){
    console.log(key, component)
    app.component(key, component)
    }
  }

  // 注册图标
  for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
    app.component(key, component)
  }
  app.mount('#app');
</script>

</html>